﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Practices.Unity;
using Sern.Abstracts.Booster;
using Sern.Abstracts.Collector;
using Sern.Abstracts.Common;
using Sern.Abstracts.Filter;
using Sern.Abstracts.Splitter;
using Sern.Abstracts.Tracker.DataContracts;
using Sern.Abstracts.Tracker.ServiceContracts;
using Sern.Common;

namespace Sern.ServiceImplementation.MapReduce
{
    public class LocalRegistrar : IComponentRegistrar
    {
        private readonly static IDictionary<Type, object> MapReduceTypeCache = new Dictionary<Type, object>();

        public void Register(IUnityContainer container)
        {


            //Registers tracker factory
            container.RegisterInstance<Func<IClient<ITracker>>>(
                () =>
                    MapReduceClientTrackerProxyFactory.CreateProxy2(
                        container.Resolve<IBindingFactory>()[ClientType.Tracker](),
                        //container.Resolve<string>("ClientTrackerUri")));ClientHostName
                        container.Resolve<string>("ClientHostName")));

            var dynamicTrackerInjectionFactory = new InjectionFactory(
                (ctr, type, name) =>
                    {
                        var trackerType = type.GetGenericArguments()[0].GetGenericArguments()[0];
                        var proxy = typeof (MapReduceClientTrackerProxyFactory).GetMethod("CreateTrackerProxy")
                            .MakeGenericMethod(trackerType)
                            .Invoke(null, new object[] {ComponentLocator.Unity.Resolve<Func<IClient<ITracker>>>()});

                        return proxy;
                    }
                );

            container.RegisterType(
                typeof (Func<IClient<ITrackerLogging>>),
                    dynamicTrackerInjectionFactory 
                );

            container.RegisterType(
                typeof(Func<IClient<ITrackerTelemetryReceiver>>),
                    dynamicTrackerInjectionFactory);

            ////Registers tracker telemetry factory
            //container.RegisterInstance<Func<IClient<ITrackerTelemetryReceiver>>>(
            //    () =>
            //        MapReduceClientTrackerProxyFactory.CreateTelemetryProxy(
            //            container.Resolve<IBindingFactory>()[ClientType.TrackerTelemetry](),
            //            container.Resolve<Func<IClient<ITracker>>>()()
            //                .Proxy
            //                .FindActiveClientInfo(
            //                    ComponentLocator.Unity.Resolve<Guid>("ClientJobId"),
            //                    ClientType.TrackerTelemetry,
            //                    Guid.Empty,
            //                    Guid.Empty,
            //                    null,
            //                    null).Uri));

            //Map reduce proxy registration - it requires the instance object in order to do key affinity for filtering.
            container.RegisterInstance<Action<object, object>>(
                "SplitterInvoker",
                (key, value) =>
                {
                    using (var mrProxy = (typeof(MapReduceProxyFactory<,,>).MakeGenericType(
                                        typeof(ISplitter<,>).MakeGenericType(key.GetType(), value.GetType()),
                                        key.GetType(),
                                        value.GetType())
                                 as dynamic)
                                    .CreateMapReduceProxy(container.Resolve<Func<IClient<ITracker>>>(), key))
                    {
                        mrProxy.Proxy.Handle(key, value);
                    }
                });

            //Map reduce proxy registration - it requires the instance object in order to do key affinity for filtering.
            container.RegisterInstance<Action<object, object>>(
                "FilterInvoker",
                (key, value) =>
                {
                    using (var mrProxy = (typeof(MapReduceProxyFactory<,,>).MakeGenericType(
                                typeof(IFilter<,>).MakeGenericType(key.GetType(), value.GetType()), key.GetType(),
                                value.GetType())
                                 as dynamic)
                                    .CreateMapReduceProxy(container.Resolve<Func<IClient<ITracker>>>(), key))
                    {
                        mrProxy.Proxy.Handle(key, value);
                    }
                });

            //Note: Booster will just need to resolve against the container to iterate. It won't be as natural, but hate to write a special map reduce for it.
            container.RegisterInstance<Action<object, object>>(
                "BoosterInvoker",
                (key, value) =>
                {
                    using (var mrProxy = (typeof(MapReduceProxyFactory<,,>).MakeGenericType(
                        typeof(IBooster<,>).MakeGenericType(key.GetType(), value.GetType()), key.GetType(),
                        value.GetType())
                                          as dynamic)
                        .CreateMapReduceProxy(container.Resolve<Func<IClient<ITracker>>>(), key))
                    {
                        mrProxy.Proxy.Handle(key, value);
                    }
                });

            container.RegisterInstance<Action<object, object>>(
                "BoosterInvoker",
                (key, value) =>
                {
                    using (var mrProxy = (typeof(MapReduceProxyFactory<,,>).MakeGenericType(
                        typeof(IBooster<,>).MakeGenericType(key.GetType(), value.GetType()), key.GetType(),
                        value.GetType())
                                          as dynamic)
                        .CreateMapReduceProxy(container.Resolve<Func<IClient<ITracker>>>(), key))
                    {
                        mrProxy.Proxy.Handle(key, value);
                    }
                });

            container.RegisterInstance<Action<object, object>>(
                "CollectorInvoker",
                (key, value) =>
                {
                    using (var mrProxy = (typeof(MapReduceProxyFactory<,,>).MakeGenericType(
                                typeof(ICollector<,>).MakeGenericType(key.GetType(), value.GetType()), key.GetType(),
                                value.GetType())
                                 as dynamic)
                                    .CreateMapReduceProxy(container.Resolve<Func<IClient<ITracker>>>(), key))
                    {
                        mrProxy.Proxy.Handle(key, value);
                    }
                });

            //TrackerLogging is not registered yet, may return a null if not found!

            container.RegisterType(typeof(ISplitter<,>),
                                   new InjectionFactory(
                                       (cntnr, type, name) =>
                                       {
                                           var mapReduceInterfaceType =
                                               typeof(ISplitter<,>)
                                                .MakeGenericType(type.GetGenericArguments());
                                           if (!MapReduceTypeCache.ContainsKey(mapReduceInterfaceType))
                                           {
                                               MapReduceTypeCache.Add(
                                                   mapReduceInterfaceType,
                                                    Activator.CreateInstance(
                                                       typeof(MapReduce<,>).MakeGenericType(type.GetGenericArguments()),
                                                           cntnr.Resolve<Func<IClient<ITrackerTelemetryReceiver>>>(),
                                                           cntnr.Resolve<Func<IClient<ITrackerLogging>>>(),
                                                           new Func<Guid, Guid, bool>((key, redundancyKey) =>
                                                                 container.Resolve<Func<IClient<ITracker>>>()().Proxy
                                                                     .IsMinimumRedundancySatisfied(
                                                                        cntnr.Resolve<Guid>("ClientJobId"), 
                                                                        key, 
                                                                        redundancyKey, 
                                                                        ClientType.Filter)),
                                                           cntnr.Resolve<Guid>("ClientJobId"),
                                                           cntnr.Resolve<Guid>("ClientId"),
                                                           cntnr.Resolve<Action<object, object>>("FilterInvoker"),
                                                           cntnr.Resolve(
                                                               typeof(Action<,,>).MakeGenericType(
                                                                   type.GetGenericArguments()
                                                                       .Concat(new[]
                                                                                       {
                                                                                           typeof (
                                                                                               Action
                                                                                               <object, object
                                                                                               >)
                                                                                       }).ToArray()),
                                                                "ClientMapReduceDelegate"),
                                                           cntnr.Resolve<LogLevel>("ClientLogLevel"))
                                                    );
                                           }

                                           return MapReduceTypeCache[mapReduceInterfaceType];
                                       }
                                       ));
            
            container.RegisterType(typeof(IFilter<,>),
                                   new InjectionFactory(
                                       (cntnr, type, name) =>
                                       {
                                           var mapReduceInterfaceType =
                                               typeof(IFilter<,>)
                                                .MakeGenericType(type.GetGenericArguments());
                                           if (!MapReduceTypeCache.ContainsKey(mapReduceInterfaceType))
                                           {
                                               MapReduceTypeCache.Add(
                                                   mapReduceInterfaceType,
                                                    Activator.CreateInstance(
                                                       typeof(MapReduce<,>).MakeGenericType(
                                                            type.GetGenericArguments()),
                                                           cntnr.Resolve<Func<IClient<ITrackerTelemetryReceiver>>>(),
                                                           cntnr.Resolve<Func<IClient<ITrackerLogging>>>(),
                                                           new Func<Guid, Guid, bool>((key, redundancyKey) =>
                                                                 container.Resolve<Func<IClient<ITracker>>>()().Proxy
                                                                     .IsMinimumRedundancySatisfied(
                                                                        cntnr.Resolve<Guid>("ClientJobId"),
                                                                        key, 
                                                                        redundancyKey, 
                                                                        ClientType.Booster)), cntnr.Resolve<Guid>("ClientJobId"),
                                                           cntnr.Resolve<Guid>("ClientId"),
                                                           cntnr.Resolve<Action<object, object>>("BoosterInvoker"),
                                                           cntnr.Resolve(
                                                               typeof(Action<,,>).MakeGenericType(
                                                                   type.GetGenericArguments()
                                                                       .Concat(new[]
                                                                                       {
                                                                                           typeof (
                                                                                               Action
                                                                                               <object, object
                                                                                               >)
                                                                                       }).ToArray()),
                                                                "ClientMapReduceDelegate"),
                                                           cntnr.Resolve<LogLevel>("ClientLogLevel"))
                                                        );
                                           }

                                           return MapReduceTypeCache[mapReduceInterfaceType];
                                       }
                                       ));

            container.RegisterType(typeof(IBooster<,>),
                       new InjectionFactory(
                           (cntnr, type, name) =>
                           {
                               var mapReduceInterfaceType =
                                   typeof(ISplitter<,>)
                                    .MakeGenericType(type.GetGenericArguments());
                               if (!MapReduceTypeCache.ContainsKey(mapReduceInterfaceType))
                               {
                                   MapReduceTypeCache.Add(
                                       mapReduceInterfaceType,
                                        Activator.CreateInstance(
                                           typeof(MapReduce<,>).MakeGenericType(type.GetGenericArguments()),
                                               cntnr.Resolve<Func<IClient<ITrackerTelemetryReceiver>>>(),
                                               cntnr.Resolve<Func<IClient<ITrackerLogging>>>(),
                                                           new Func<Guid, Guid, bool>((key, redundancyKey) =>
                                                                 container.Resolve<Func<IClient<ITracker>>>()().Proxy
                                                                     .IsMinimumRedundancySatisfied(
                                                                        cntnr.Resolve<Guid>("ClientJobId"),
                                                                        key,
                                                                        redundancyKey,
                                                                        ClientType.Splitter)),
                                               cntnr.Resolve<Guid>("ClientJobId"),

                                               cntnr.Resolve<Guid>("ClientId"),
                                               cntnr.Resolve<Action<object, object>>("SplitterInvoker"),
                                                           cntnr.Resolve(
                                                               typeof(Action<,,>).MakeGenericType(
                                                                   type.GetGenericArguments()
                                                                       .Concat(new[]
                                                                                       {
                                                                                           typeof (
                                                                                               Action
                                                                                               <object, object
                                                                                               >)
                                                                                       }).ToArray()),
                                                                "ClientMapReduceDelegate"),
                                               cntnr.Resolve<LogLevel>("ClientLogLevel"))
                                        );
                               }

                               return MapReduceTypeCache[mapReduceInterfaceType];
                           }
                           ));
        }
    }
}
